package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.article.HotArticleConstants;
import com.heima.model.mess.UpdateArticleMess;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

@Component
@Slf4j
public class UpdateHotArticleJob {

    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    HotArticleService hotArticleService;

    @XxlJob("updateHotArticleJob")
    public ReturnT updateHotArticleHandler(String params){
        log.info("热文章分值更新 调度任务开始执行....");
        //  定时更新文章热度
        //1.获取redis 行为列表中待处理数据
        List<UpdateArticleMess> articleMessList = getUpdateArticleMesses();
        if(CollectionUtils.isEmpty(articleMessList)){
            log.info("没人看没人看");
            return ReturnT.SUCCESS;
        }
        //将数据按照文章分组  进行聚合统计  得到待更新的数据列表
        List<ArticleVisitStreamMess> waitUpdateScoreData = getArticleVisitStreamMesses(articleMessList);
        if(CollectionUtils.isEmpty(waitUpdateScoreData)){
            log.info("没人看没人看");
            return ReturnT.SUCCESS;
        }

        //更新数据库文章分值
        waitUpdateScoreData.forEach(hotArticleService::updateApArticle);
        log.info("热文章分值更新 调度任务完成....");
        return ReturnT.SUCCESS;
    }

    /**按照文章分组进行聚合统计 方法实现
     *
     * @param articleMessList
     * @return
     */
    private List<ArticleVisitStreamMess> getArticleVisitStreamMesses(List<UpdateArticleMess> articleMessList) {
        //空集合
        List<ArticleVisitStreamMess> waitUpdateScoreData = new ArrayList<>();
        Map<Long, List<UpdateArticleMess>> map = articleMessList.stream()
                //根据文章id分组
                .collect(Collectors.groupingBy(UpdateArticleMess::getArticleId));
        //遍历集合
        map.forEach((articleId,messList)->{
            Optional<ArticleVisitStreamMess> reduceResult =
                    messList.stream()
                            .map(articleMes->{
                                ArticleVisitStreamMess visitStreamMess = new ArticleVisitStreamMess();
                                visitStreamMess.setArticleId(articleId);
                                //根据键匹配 case值设置行为数量
                                switch (articleMes.getType()){
                                    case LIKES:
                                        //设置点赞数量
                                        visitStreamMess.setLike(articleMes.getAdd());
                                        break;
                                    case VIEWS:
                                        //设置阅读数量
                                        visitStreamMess.setView(articleMes.getAdd());
                                        break;
                                    case COMMENT:
                                        //设置评论数量
                                        visitStreamMess.setComment(articleMes.getAdd());
                                        break;
                                    case COLLECTION:
                                        //设置收藏数量
                                        visitStreamMess.setCollect(articleMes.getAdd());
                                        break;
                                    default:
                                }
                                return visitStreamMess;
                            }).reduce(new BinaryOperator<ArticleVisitStreamMess>() {
                                @Override
                                public ArticleVisitStreamMess apply(ArticleVisitStreamMess ames1, ArticleVisitStreamMess ames2) {
                                    ames1.setLike(ames1.getLike()+ames2.getLike());
                                    ames1.setView(ames1.getView()+ames2.getView());
                                    ames1.setComment(ames1.getComment()+ames2.getComment());
                                    ames1.setCollect(ames1.getCollect()+ames2.getComment());
                                    return ames1;
                                }
                            });
            if(reduceResult.isPresent()){
                //聚合结果
                ArticleVisitStreamMess visitStreamMess = reduceResult.get();
                log.info("热点文章计算聚合结果===> {}" ,visitStreamMess);
                waitUpdateScoreData.add(visitStreamMess);
            }
        });
        return waitUpdateScoreData;
    }


    /**获取redis行为列表待处理数据方法实现
     *
     * @return
     */
    private List<UpdateArticleMess> getUpdateArticleMesses() {
        //获取redis行为表中的待处理数据
        ListOperations<String, String> listOperations = redisTemplate.opsForList();
        //得到当前待处理数据的数据量
        Long size = listOperations.size(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST);
        //采用管道命令 让多个命令保证原子性
        List<Object> result = redisTemplate.executePipelined(new RedisCallback<List<UpdateArticleMess>>() {
            @Override
            public List<UpdateArticleMess> doInRedis(RedisConnection connection) throws DataAccessException {
                //开启管道命令
                connection.openPipeline();
                //获取0-size-1的所有数据
                connection.lRange(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), 0, (size - 1));
                //截取size到-1的所有数据 (全部)
                connection.lTrim(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), size, -1);
                return null;
            }
        }, RedisSerializer.string());

        if(result.size()>0){
            List<String> listData = (List<String>) result.get(0);

          return   listData.stream().map(str-> JSON.parseObject(str,UpdateArticleMess.class)).collect(Collectors.toList());
        }

        return null;
    }
}